
#include <cstdio>
#include <BSocket>
#include "member_BSocket.h"

#ifdef linux
#include <arpa/inet.h>
#include <sys/ioctl.h>
#include <net/if.h>
#endif

using namespace BWE;

extern BList<BSocket*>	app_sockets;

#define member					(*(member_BSocket*)_ptr)
#define member_allocate()		_ptr = new member_BSocket(this)
#define member_release()		delete (member_BSocket*)_ptr

BString BSocket::localAddr()
{
	BString addr;
	localAddr(addr);
	return addr;
}
bool BSocket::localAddr(BString& addr)
{
#ifdef _WIN32
	static char* loc_ip = NULL;
	char loc_ip_arr[1028] = { 0 };
	DWORD ret = 0;
	WSADATA wsaData;
	if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
	{
		ret = WSAGetLastError();
		if (ret != WSA_IO_PENDING)
		{
			printf("WSAStartup failed.\n");
			addr = loc_ip;
			return true;
		}
	}

	SOCKET sct = WSASocketA(AF_INET, SOCK_DGRAM, 0, 0, 0, 0);
	if (sct == SOCKET_ERROR)
	{
		printf("create socket failed.\n");
		return false;
	}

	INTERFACE_INFO interfaceList[50];
	ZeroMemory(interfaceList, sizeof(interfaceList));
	DWORD bytesReturned;

	if (WSAIoctl(sct, SIO_GET_INTERFACE_LIST, NULL, 0, interfaceList, sizeof(interfaceList), &bytesReturned, 0, 0) == SOCKET_ERROR)
	{
		ret = WSAGetLastError();
		if (ret != WSA_IO_PENDING)
		{
			printf("get ioctrl failed.\n");
			return false;
		}
	};

	int num = bytesReturned / sizeof(INTERFACE_INFO);
	for (int i = 0; i < num; i++)
	{
		sockaddr_in* pAddress = (sockaddr_in*)&(interfaceList[i].iiAddress);
		loc_ip = inet_ntoa(pAddress->sin_addr);
		if (strcmp("127.0.0.1", loc_ip) == 0)
		{
			continue;
		}
		strcat(loc_ip_arr, loc_ip);
		if (i == num - 1)
		{
			break;
		}
		strcat(loc_ip_arr, ",");
	}
	strcpy(loc_ip, loc_ip_arr);
	WSACleanup();
	addr = loc_ip;
	return true;
#endif

#ifdef linux
	char* loc_ip = NULL;
	int sock_fd = -1, interface = -1;
	ifconf ifc;
	ifreq buf[INET_ADDRSTRLEN];

	if ((sock_fd = ::socket(AF_INET, SOCK_DGRAM, 0)) < 0)
	{
		return false;
	}

	ifc.ifc_len = sizeof(buf);
	ifc.ifc_buf = (caddr_t)buf;

	if (::ioctl(sock_fd, SIOCGIFCONF, (char*)&ifc) != 0)
	{
		return false;
	}

	interface = INET_ADDRSTRLEN;
	while (interface-- > 0)
	{
		if (::ioctl(sock_fd, SIOCGIFADDR, (char*)&buf[interface]) == 0)
		{
			loc_ip = NULL;
			loc_ip = ::inet_ntoa(((struct sockaddr_in*)&buf[interface].ifr_addr)->sin_addr);
			if (loc_ip && strcmp("127.0.0.1", loc_ip))
			{
				break;
			}
		}
	}
	::close(sock_fd);
	addr = loc_ip;
	return true;
#endif
	return false;
}

BSocket::BSocket()
{
	member_allocate();
	app_sockets.append(this);
}
BSocket::~BSocket()
{
	app_sockets.remove(this);
	if (member.sock)
		close();
	member_release();
}

bool BSocket::flush() const
{
	if (member.sock)
	{
#ifdef linux
		return false;
#endif
#ifdef _WIN32
		return false;
#endif
	}
	return true;
}
bool BSocket::close()
{
	if (member.sock)
	{
#ifdef linux
		int ret = ::close(member.sock);
		if (ret == -1)
		{
			puts("close socket");
			return false;
		}
#endif

#ifdef _WIN32
		::closesocket(member.sock);
#endif
		member.sock = 0;
		member.state = State_Closed;
		this->emit(Signal_Disconnected);
		this->emit(Signal_StateChanged, State_Closed);
	}
	return true;
}

bool BSocket::valid() const
{
	return member.sock > 0;
}

const BString& BSocket::address() const
{
	return member.address;
}
int BSocket::port() const
{
	return member.port;
}

BSocket::Error BSocket::error() const
{
	return (Error)member.error;
}
const char* BSocket::errorStr() const
{
	return member.errorStr;
}

BSocket::State BSocket::state() const
{
	return member.state;
}

int BSocket::handle() const
{
	return (int)(member.sock);
}

